#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/tty_driver.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>

#define DRIVER_DBG
#ifdef DRIVER_DBG
#define driver_dbg(fmt, ...) printk("[%s][%d]" fmt "\r\n", __func__, __LINE__, ##__VA_ARGS__)
#else
#define driver_dbg(fmt, ...)
#endif

#define DRIVER_NAME "driver_virtual_tty"
#define DRIVER_DEV_NAME "ttyVir"
#define DRIVER_MAX_TTY_DEVS (5)

static struct tty_driver *virtual_tty_driver;

static int driver_virtual_tty_platform_probe(struct platform_device *dev)
{
    driver_dbg("");

    return 0;
}

static int driver_virtual_tty_platform_remove(struct platform_device *dev)
{
    driver_dbg("");

    return 0;
}

static const struct of_device_id tty_platform_table[] = {
    {.compatible = "driver,virtual_tty"},
    {},
};

static struct platform_driver virtual_tty_dri = {
    .probe = driver_virtual_tty_platform_probe,
    .remove = driver_virtual_tty_platform_remove,
    .driver = {
        .name = DRIVER_DEV_NAME,
        .of_match_table = tty_platform_table,
    },
};

static int __init driver_virtual_tty_init(void)
{
    int ret = 0;

    driver_dbg("");

    virtual_tty_driver = tty_alloc_driver(DRIVER_MAX_TTY_DEVS, TTY_DRIVER_DYNAMIC_ALLOC);
    if (IS_ERR(virtual_tty_driver))
    {
        driver_dbg("tty_alloc_driver failed, %ld", PTR_ERR(virtual_tty_driver));
        return PTR_ERR(virtual_tty_driver);
    }

    // virtual_tty_driver->driver_name = DRIVER_NAME;
    virtual_tty_driver->name = DRIVER_DEV_NAME;

    ret = tty_register_driver(virtual_tty_driver);
    if (ret)
    {
        driver_dbg("tty_register_driver failed, %d", ret);
        goto err_put_tty;
    }

    ret = platform_driver_register(&virtual_tty_dri);
    if (ret)
    {
        driver_dbg("platform_driver_register err:%d", ret);
        goto err_unret_tty;
    }

    return 0;

err_unret_tty:
    tty_unregister_driver(virtual_tty_driver);
err_put_tty:
    tty_driver_kref_put(virtual_tty_driver);

    return ret;
}

static void __exit driver_virtual_tty_exit(void)
{
    driver_dbg("");
    platform_driver_unregister(&virtual_tty_dri);
    tty_unregister_driver(virtual_tty_driver);
    tty_driver_kref_put(virtual_tty_driver);
}

module_init(driver_virtual_tty_init);

module_exit(driver_virtual_tty_exit);

MODULE_AUTHOR("ljq");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("virtual tty driver");
